﻿using huqiang.UIEvent;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

namespace huqiang.Core.HGUI
{
    //[ExecuteAlways]
    /// <summary>
    /// HGUI呈现器
    /// </summary>
    public class HGUIRender:MonoBehaviour
    {
        internal static List<HGUIRender> AllRender = new List<HGUIRender>();
        public static void GetContextRender(Camera cam,List<HGUIRender> buf)
        {
            var main = Camera.main;
            if (AllRender != null)
                for (int i = 0; i < AllRender.Count; i++)
                {
                    var r = AllRender[i];
                    if (cam == r.TargetCamera)
                    {
                        buf.Add(r);
                    }
                    else if (r.TargetCamera == null)
                        if (cam == main)
                            buf.Add(r);
                }
#if UNITY_EDITOR
            if (!Application.isPlaying)
            {
                HGUIRender[] renders = UnityEngine.Resources.FindObjectsOfTypeAll<HGUIRender>();
                if (renders != null)
                {
                    for (int i = 0; i < renders.Length; i++)
                    {
                        var r = renders[i];
                        if (cam == r.TargetCamera)
                            buf.Add(r);
                        else if (r.TargetCamera == null)
                            if (cam == main)
                                buf.Add(r);
                    }
                }
            }
#endif
        }
        [HideInInspector]
        public string uid;
        [HideInInspector]
        public int ContextID;
        public string GetGuid()
        {
            if (uid == null|uid=="")
                uid = Guid.NewGuid().ToString();
            return uid;
        }
        [HideInInspector]
        public Transform trans;
        //[NonSerialized]
        //[HideInInspector]
        public HCanvas canvas;
        [HideInInspector]
        public Mesh mesh;
        [HideInInspector]
        public List<Material> materials = new List<Material>();
        [HideInInspector]
        public Texture[] textures = new Texture[128*8];
        [HideInInspector]
        internal Batchs batchs;

        public ScaleType scaleType = ScaleType.FillY;
        /// <summary>
        /// 目标相机,如果为空则使用主相机
        /// </summary>
        public Camera TargetCamera;
        /// <summary>
        /// 默认设计尺寸
        /// </summary>
        public Vector2 DesignSize = new Vector2(1920, 1080);
        /// <summary>
        /// 距离相机镜头的距离
        /// </summary>
        public float NearPlane = 0.05f;
        public RenderMode renderMode;
        /// <summary>
        /// 暂停用户事件
        /// </summary>
        public bool PauseEvent;
        /// <summary>
        /// 暂停所有更新
        /// </summary>
        public bool Pause;
        public bool MultiBatch;

        public int renderQueue = 3100;
        [HideInInspector]
        public Vector3 renderPos;
        [HideInInspector]
        public Quaternion renderRotation;
        public Vector3 renderScale=Vector3.one;
        public float UIScreenWidth = 1920;
        public float UIScreenHeight = 1080;
        public void Awake()
        {
            HCanvas.RegCanvas(this);
            AllRender.Add(this);
        }
        public virtual void Start()
        {
            trans = transform;
        }
        protected virtual void OnDestroy()
        {
            AllRender.Remove(this);
            HCanvas.ReleaseCanvas(this);
            canvas = null;
            ContextID = 0;
        }
        internal void Batch()
        {
            if (batchs == null)
                batchs = new Batchs();
            if (canvas != null)
            {
                canvas.Pause = Pause;
                canvas.PauseEvent = PauseEvent;
                canvas.renderQueue = renderQueue;
                canvas.Batch(batchs);
            }
        }
        internal void Apply()
        {
            if (canvas != null)
            {
                canvas.ApplyMeshRenderer(this);
            }
        }
        public  void Scaling()
        {
            var cam = TargetCamera;
            if (cam == null)
                cam = Camera.main;
            Vector2 pSize = new Vector2(cam.pixelWidth, cam.pixelHeight);
            switch (scaleType)
            {
                case ScaleType.None:
                    UIScreenWidth = DesignSize.x;
                    UIScreenHeight = DesignSize.y;
                    break;
                case ScaleType.FillX:
                    float sx = pSize.x / DesignSize.x;
                    canvas.localScale = new Vector3(sx, sx, sx);
                    UIScreenWidth = DesignSize.x;
                    UIScreenHeight = pSize.y / pSize.x * UIScreenWidth;
                    break;
                case ScaleType.FillY:
                    float sy = pSize.y / DesignSize.y;
                    canvas.localScale = new Vector3(sy, sy, sy);
                    UIScreenHeight = DesignSize.y;
                    UIScreenWidth = pSize.x / pSize.y * UIScreenHeight;
                    break;
                case ScaleType.FillXY:
                    sx = pSize.x / DesignSize.x;
                    sy = pSize.y / DesignSize.y;
                    if (sx < sy)
                    {
                        canvas.localScale = new Vector3(sx, sx, sx);
                        UIScreenWidth = DesignSize.x;
                        UIScreenHeight = pSize.y / pSize.x * UIScreenWidth;
                    }
                    else 
                    {
                        canvas.localScale = new Vector3(sy, sy, sy);
                        UIScreenHeight = DesignSize.y;
                        UIScreenWidth = pSize.x / pSize.y * UIScreenHeight;
                    }
                    break;
                case ScaleType.Cover:
                    sx = pSize.x / DesignSize.x;
                    sy = pSize.y / DesignSize.y;
                    if (sx < sy)
                    {
                        canvas.localScale = new Vector3(sy, sy, sy);
                        UIScreenHeight = DesignSize.y;
                        UIScreenWidth = pSize.x / pSize.y * UIScreenHeight;
                    }
                    else
                    {
                        canvas.localScale = new Vector3(sx, sx, sx);
                        UIScreenWidth = DesignSize.x;
                        UIScreenHeight = pSize.y / pSize.x * UIScreenWidth;
                    }
                    break;
            }
        }
        public void ApplyToCamera()
        {
            Scaling();
            if (canvas != null)
                canvas.m_sizeDelta = new Vector2(UIScreenWidth,UIScreenHeight);
            switch (renderMode)
            {
                case RenderMode.ScreenSpaceOverlay:
                    //Vector2 pSize = new Vector2(UIScreenWidth, UIScreenHeight);
                    //UIElement.Scaling(canvas, scaleType, pSize, DesignSize);
                    //UIElement.MarginEx(canvas, new Margin(), new Vector2(0.5f, 0.5f), pSize);
                    OverCamera(Camera.main);
                    break;
                case RenderMode.ScreenSpaceCamera:
                    if (TargetCamera != null)
                        OverCamera(TargetCamera);
                    else OverCamera(Camera.main);
                    //transform.position = renderPos;
                    //transform.rotation = renderRotation;
                    //transform.localScale = canvas.localScale;
                    break;
                case RenderMode.WorldSpace:
                    break;
            }
        }
        void OverCamera(Camera cam)
        {
            if (cam != null)
            {
                //float w = cam.pixelWidth;
                float h = cam.pixelHeight;
                float near = cam.nearClipPlane + NearPlane;
                if (cam.orthographic)
                {
                    float os = cam.orthographicSize * 2;
                    float s = os / (float)h;
                    renderScale = new Vector3(s, s, s);
                    Vector3 pos = cam.transform.position;
                    Vector3 forward = cam.transform.forward;
                    renderPos = pos + forward * near;
                }
                else
                {
                    float halfFOV = (cam.fieldOfView * 0.5f) * Mathf.Deg2Rad;
                    float height = near * Mathf.Tan(halfFOV);
                    float s = height / h * 2;
                    renderScale = new Vector3(s, s, s);
                    Vector3 pos = cam.transform.position;
                    Vector3 forward = cam.transform.forward;
                    renderPos = pos + forward * near;
                }
               renderRotation = cam.transform.rotation;
            }
        }
        public enum FPSMode
        {
            Full = 0,
            F30 = 30,
            F60 = 60,
            F120=120
        }
        public FPSMode FPS = FPSMode.Full;
        float UpTime;
        bool up;
        private void Update()
        {
            if (canvas == null)
                return;
            up = false;
            if (FPS == FPSMode.Full)
            {
                up = true;
            }
            else
            {
                float ut = 1 / (float)FPS;
                UpTime += Time.deltaTime;
                if (UpTime >= ut)
                {
                    UpTime = 0;
                    up = true;
                }
            }
            if (up)
            {
                ApplyToCamera();
                canvas.UpdateMesh();
                HTextGenerator.RebuildUV();
                HTextGenerator.End();
                if (!MultiBatch)
                    Batch();
            }
        }
        internal void Setup()
        {
            if (canvas == null)
                return;
            up = false;
            if (FPS == FPSMode.Full)
            {
                up = true;
            }
            else
            {
                float ut = 1 / (float)FPS;
                UpTime += Time.deltaTime;
                if (UpTime >= ut)
                {
                    UpTime = 0;
                    up = true;
                }
            }
            if (up)
            {
                ApplyToCamera();
                canvas.UpdateMesh();
                HTextGenerator.RebuildUV();
                HTextGenerator.End();
                if (!MultiBatch)
                    Batch();
            }
        }
        internal void SubBatch()
        {
            if(up)
            {
                if (MultiBatch)
                {
                    if (canvas != null)
                    {
                        canvas.Pause = Pause;
                        canvas.PauseEvent = PauseEvent;
                        canvas.renderQueue = renderQueue;
                        canvas.Batch(0);
                    }
                }
            }
        }
        private void LateUpdate()
        {
            if(up)
            {
                Apply();
                up = false;
            }
        }
        public Vector2 ScreenToCanvasPos(Vector2 mPos)
        {
            if (TargetCamera != null)
                return ScreenToCanvasPos(mPos, TargetCamera);
            return ScreenToCanvasPos(mPos, Camera.main);
        }
        /// <summary>
        /// 将屏幕坐标转换为画布坐标
        /// </summary>
        /// <param name="mPos">屏幕坐标</param>
        /// <returns></returns>
        Vector2 ScreenToCanvasPos(Vector2 mPos, Camera camera)
        {
            if (renderMode == RenderMode.WorldSpace)
            {
                Vector3 a = new Vector3(-10000f, -10000f, 0);
                Vector3 b = new Vector3(0, 10000f, 0);
                Vector3 c = new Vector3(10000, -10000, 0);
                var trans = transform;
                var pos = trans.position;
                var qt = trans.rotation;
                a = qt * a + pos;
                b = qt * b + pos;
                c = qt * c + pos;//得到世界坐标的三角面
                var cam = camera;
                var ray = cam.ScreenPointToRay(mPos);
                var v = ray.origin;//cam.ScreenToWorldPoint(mPos);
                var f = ray.direction;//cam.transform.forward;
                Vector3 p = Vector3.zero;
                if (huqiang.Physics.IntersectTriangle(ref v, ref f, ref a, ref b, ref c, ref p))
                {
                    var iq = Quaternion.Inverse(qt);
                    p -= pos;
                    p = iq * p;
                    var ls = trans.localScale;
                    p.x /= ls.x;
                    p.y /= ls.y;
                    return new Vector2(p.x, p.y);
                }
                return new Vector2(-100000, -100000);
            }
            else
            {
                float w = Screen.width;
                w *= 0.5f;
                float h = Screen.height;
                h *= 0.5f;
                var cPos = Vector2.zero;
                cPos.x = mPos.x - w;
                cPos.y = mPos.y - h;
                return cPos;
            }
        }

#region
#if UNITY_EDITOR
        public TextAsset NewBytesUI;
        public TextAsset OldBytesUI;
        public string AssetName = "baseUI";
        public string dicpath;
        public string CloneName;
        public void Refresh()
        {
            if (Application.isPlaying)
            {
                return;
            }
            if (batchs == null)
                batchs = new Batchs();
            trans = transform;
            if (canvas == null)
                return;
            canvas.localPosition = Vector3.zero;
            canvas.WorldPosition = transform.position;
            canvas.UpdateMesh2(this);
            HTextGenerator.RebuildUV();
            HTextGenerator.End();
            canvas.Batch(batchs);
            canvas.ApplyMeshRenderer(this);
            ApplyToCamera();
        }
        public Vector2 ScreenToCanvasPos2(Ray ray)
        {
            Vector3 a = new Vector3(-10000f, -10000f, 0);
            Vector3 b = new Vector3(0, 10000f, 0);
            Vector3 c = new Vector3(10000, -10000, 0);
            var trans = transform;
            var pos = trans.position;
            var qt = trans.rotation;
            a = qt * a + pos;
            b = qt * b + pos;
            c = qt * c + pos;//得到世界坐标的三角面
            var v = ray.origin;//cam.ScreenToWorldPoint(mPos);
            var f = ray.direction;//cam.transform.forward;
            Vector3 p = Vector3.zero;
            if (huqiang.Physics.IntersectTriangle(ref v, ref f, ref a, ref b, ref c, ref p))
            {
                //var iq = Quaternion.Inverse(qt);
                //p -= pos;
                //p = iq * p;
                //var ls = trans.localScale;
                //p.x /= ls.x;
                //p.y /= ls.y;
                return new Vector2(p.x, p.y);
            }
            return new Vector2(-100000, -100000);
        }
        public UIElement WorldSelect(Ray ray)
        {
            //var cam = UnityEditor.SceneView.lastActiveSceneView.camera;
            var wpos = ScreenToCanvasPos2(ray);
            float lx = canvas.SizeDelta.x * -0.5f;
            float rx = -lx;
            float dy = canvas.SizeDelta.y * -0.5f;
            float ty = -dy;
            if (wpos.x >= lx & wpos.x <= rx)
                if (wpos.y >= dy & wpos.y <= ty)
                {
                    SelectElement = canvas;
                    canvas.Collection2(canvas, -1, 0,transform);
                    HGUIElement root =canvas.PipeLine[0];
                    if (root.script != null)
                    {
                        int c = root.childCount;
                        for (int i = c; i > 0; i--)
                        {
                            try
                            {
                                if (Ray(canvas.PipeLine, i, wpos))
                                {
                                    return SelectElement; 
                                }
                            }
                            catch (Exception ex)
                            {
                                UnityEngine.Debug.LogError(ex.StackTrace);
                            }
                        }
                    }
                }
            return null;
        }
        static Vector2[] Box=new Vector2[4];
        static UIElement SelectElement;
        static bool InThere(UIElement script, Vector3 pos,Quaternion q,Vector3 scale, Vector2 dot)
        {
            float w = script.m_sizeDelta.x * scale.x;
            float h = script.m_sizeDelta.y * scale.y;
            float px = script.Pivot.x;
            float py = script.Pivot.y;
            float lx = -px * w;
            float rx = lx + w;
            float dy = -py * h;
            float ty = dy + h;

            Box[0] = q * new Vector3(lx, dy) + pos;
            Box[1] = q * new Vector3(lx, ty) + pos;
            Box[2] = q * new Vector3(rx, ty) + pos;
            Box[3] = q * new Vector3(rx, dy) + pos;
            return huqiang.Physics2D.DotToPolygon(Box, dot);
        }
        static bool Ray(HGUIElement[] pipeLine, int index, Vector2 canPos)
        {
            if (!pipeLine[index].active)
                return false;
            Vector3 o = pipeLine[index].Position;
            Vector3 scale = pipeLine[index].Scale;
            Quaternion q = pipeLine[index].Rotation;

            var script = pipeLine[index].script;
            if (script != null)
            {
                bool inside = InThere(script, o, q, scale, canPos);
                if (inside)
                {
                    SelectElement = script;
                    int c = pipeLine[index].childCount;
                    if (c > 0)
                    {
                        int os = pipeLine[index].childOffset + c;
                        for (int i = 0; i < c; i++)
                        {
                            os--;
                            if (Ray(pipeLine, os, canPos))
                            {
                                return true;
                            }
                        }
                    }
                    return true;
                }
                else if (!script.Mask)
                {
                    int c = pipeLine[index].childCount;
                    if (c > 0)
                    {
                        int os = pipeLine[index].childOffset + c;
                        for (int i = 0; i < c; i++)
                        {
                            os--;
                            if (Ray(pipeLine, os, canPos))
                            {
                                return true;
                            }
                        }
                    }
                }
            }
            else
            {
                int c = pipeLine[index].childCount;
                if (c > 0)
                {
                    int os = pipeLine[index].childOffset + c;
                    for (int i = 0; i < c; i++)
                    {
                        os--;
                        if (Ray(pipeLine, os, canPos))
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }
#endif
#endregion
    }
}
